﻿/*
** Copyright 2008-2009, Ernest Laurentin (http://www.ernzo.com/)
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
**     http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
**
** File:        AtlActiveXContainer.hpp
** Version:     1.1 - Support for windowless keyboard events
**                1.2 - Reconstructed by hjx 2013-11-05
*/
#ifndef ATLACTIVEXCONTAINER_HPP
#define ATLACTIVEXCONTAINER_HPP

#pragma once

#include "SAxUtil.h"
#include <atl.mini/SComCli.h>
#include <MsHtmHst.h>

SNSBEGIN

struct IAxHostDelegate
{
	virtual HWND GetAxHostWindow() const = 0;
	virtual void OnAxActivate(IUnknown* pCtrl) = 0;
	virtual void OnAxInvalidate(LPCRECT pRect, BOOL bErase) = 0;
	virtual void OnAxSetCapture(BOOL fCapture) = 0;
	virtual HRESULT OnAxGetDC(LPCRECT pRect, DWORD grfFlags, HDC* phDC) = 0;
	virtual HRESULT OnAxReleaseDC(HDC hdc) = 0;
};

/**
 * ActiveXSite class
 */
template<class T>
class  ActiveXSite : public IOleClientSite,
	public IOleControlSite,
	public IOleInPlaceSiteWindowless,
	public IAdviseSink
{
	friend T;

public:
	ActiveXSite()
		: m_pAxHostDelegate(NULL)
		, m_bVisible(true)
		, m_bInplaceActive(false)
		, m_dwMiscStatus(0)
		, m_dwOleObjSink(0)
		, m_dwViewObjectType(0)
		, m_grfFlags(0)
		, m_bCaptured(FALSE)
		, m_bFocused(FALSE)
	{
		memset(&m_rcPos, 0, sizeof(m_rcPos));
	}

	virtual ~ActiveXSite()
	{
		Clear();
	}

	void SetAxHost(IAxHostDelegate* pAxHost)
	{
		m_pAxHostDelegate = pAxHost;
	}

	void SetExternalUIHandler(IDocHostUIHandler* pUiHandler)
	{
		m_spDocHostUIHandler = pUiHandler;
	}

	IUnknown* GetActiveXControl()
	{
		return m_spControl;
	}

	void Init(IUnknown* pControl)
	{
		m_spControl = pControl;
		m_spOleObject = pControl;
		m_spInPlaceObject = pControl;
	}

	void Clear()
	{
		if (m_spControl != NULL)
		{
			if (m_spViewObject != NULL)
			{
				m_spViewObject->SetAdvise(DVASPECT_CONTENT, 0, NULL);
				m_spViewObject.Release();
			}
			m_spOleObjectWindowless.Release();
			if (m_spInPlaceObject != NULL)
			{
				if (m_bInplaceActive)
				{
					m_spInPlaceObject->InPlaceDeactivate();
					m_bInplaceActive = false;
				}
				m_spInPlaceObject.Release();
			}
			if (m_spOleObject != NULL)
			{
				m_spOleObject->Unadvise(m_dwOleObjSink);
				m_spOleObject->SetClientSite(NULL);
				m_spOleObject->Close(OLECLOSE_NOSAVE);
				m_spOleObject.Release();
			}
			m_spControl = NULL;
		}
	}

	HRESULT InitControl(IStream* pStream = NULL)
	{
		if (m_spControl == 0) return E_NOINTERFACE;
		HRESULT hr = E_POINTER;
		SComQIPtr<IPersistStreamInit> spPSI(m_spControl);
		if (spPSI != NULL)
		{
			if (pStream != NULL)
				hr = spPSI->Load(pStream);
			else
				hr = spPSI->InitNew();
		}
		else if (pStream)
		{
			SComQIPtr<IPersistStream> spPS(m_spOleObject);
			if (spPS)
				hr = spPS->Load(pStream);
		}
		return hr;
	}

	HRESULT ActivateAx(IStream* pStream = NULL)
	{
		if (m_spOleObject == 0) return E_UNEXPECTED;
		HRESULT hr;

		// this must be set even before calling IPersistStreamInit::InitNew
		hr = m_spOleObject->GetMiscStatus(DVASPECT_CONTENT, &m_dwMiscStatus);
		if (m_dwMiscStatus & OLEMISC_SETCLIENTSITEFIRST) {
			hr = m_spOleObject->SetClientSite(static_cast<IOleClientSite*>(this));
			if (FAILED(hr)) return hr;
		}

		hr = InitControl(pStream);
		if (FAILED(hr))
		{
			if (m_dwMiscStatus & OLEMISC_SETCLIENTSITEFIRST)
				m_spOleObject->SetClientSite(NULL);
			return hr;
		}

		if (0 == (m_dwMiscStatus & OLEMISC_SETCLIENTSITEFIRST)) {
			hr = m_spOleObject->SetClientSite(static_cast<IOleClientSite*>(this));
			if (FAILED(hr)) return hr;
		}

		hr = DoInplaceActivate();
		return hr;
	}

	bool HitTest(const POINT& pt) const
	{
		if (m_spViewObject != NULL && m_dwViewObjectType == 7) {
			DWORD dwHitResult = HITRESULT_OUTSIDE;
			m_spViewObject->QueryHitPoint(DVASPECT_CONTENT, &m_rcPos, pt, 0, &dwHitResult);
			return (dwHitResult == HITRESULT_HIT);
		}
		// else
		return (::PtInRect(&m_rcPos, pt) != FALSE);
	}

	bool InsideClippingRegion(const RECT& rcClip) const
	{
		// true when top/let or bottom/right corners intersect into client area
		const POINT* pts = (const POINT*)(&m_rcPos);
		const POINT* ptc = (const POINT*)(&rcClip);
		return (::PtInRect(&rcClip, pts[0]) || ::PtInRect(&rcClip, pts[1]) ||
			::PtInRect(&m_rcPos, ptc[0]) || ::PtInRect(&m_rcPos, ptc[1]));
	}

	HRESULT DoVerb(long verb)
	{
		HRESULT hr = E_FAIL;
		if ((m_dwMiscStatus & (OLEMISC_INVISIBLEATRUNTIME | OLEMISC_NOUIACTIVATE)) != 0)
		{
			hr = S_FALSE;
		}
		else if (m_spOleObject != NULL)
		{
			hr = m_spOleObject->DoVerb(verb, NULL, static_cast<IOleClientSite*>(this), 0, m_pAxHostDelegate->GetAxHostWindow(), &m_rcPos);
			if (verb == OLEIVERB_INPLACEACTIVATE && SUCCEEDED(hr))
			{
				m_bInplaceActive = true;
			}
			else {
				hr = E_UNEXPECTED;
			}
		}
		return hr;
	}

	LRESULT Draw(HDC hDC, LPCRECT lpClipRect)
	{
		LRESULT lResult = S_FALSE;
		if (lpClipRect && !InsideClippingRegion(*lpClipRect)) {
			return lResult;
		}
		if (m_bVisible && m_spViewObject != NULL) {
			RECTL rcPos = { m_rcPos.left,m_rcPos.top,m_rcPos.right,m_rcPos.bottom };
			lResult = m_spViewObject->Draw(DVASPECT_CONTENT, -1, NULL, NULL, NULL, hDC,
				&rcPos, NULL, NULL, 0);
		}
		return lResult;
	}

	HRESULT FireAmbientPropertyChange(DISPID dispChanged)
	{
		HRESULT hr = S_OK;
		SComQIPtr<IOleControl, &__uuidof(IOleControl)> spOleControl(m_spControl);
		if (spOleControl != NULL)
			hr = spOleControl->OnAmbientPropertyChange(dispChanged);
		return hr;
	}

	LRESULT OnWindowMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
	{
		LRESULT lResult = 0;
		if (m_bInplaceActive && m_spOleObjectWindowless != NULL)
		{
			m_spOleObjectWindowless->OnWindowMessage(uMsg, wParam, lParam, &lResult);
		}
		return lResult;
	}

	STDMETHOD(QueryInterface2)(REFIID iid, void** object)
	{
		HRESULT hr = S_OK;
		*object = NULL;
		if (iid == IID_IOleClientSite) {
			*object = static_cast<IOleClientSite*>(this);
		}
		else if (iid == IID_IOleControlSite) {
			*object = static_cast<IOleControlSite*>(this);
		}
		else if (iid == IID_IOleInPlaceSite) {
			*object = static_cast<IOleInPlaceSite*>(this);
		}
		else if (iid == IID_IOleInPlaceSiteEx) {
			*object = static_cast<IOleInPlaceSiteEx*>(this);
		}
		else if (iid == IID_IOleInPlaceSiteWindowless) {
			*object = static_cast<IOleInPlaceSiteWindowless*>(this);
		}
		else if (iid == IID_IAdviseSink) {
			*object = static_cast<IAdviseSink*>(this);
		}
		else if (iid == IID_IDocHostUIHandler && m_spDocHostUIHandler) {
			*object = m_spDocHostUIHandler;
		}
		else {
			hr = E_NOINTERFACE;
		}
		return hr;
	}

	///////////////////////////////////////////////////////////////////////////
	// IOleClientSite
	STDMETHOD(SaveObject)(void)
	{
		ATLTRACENOTIMPL(_T("ActiveXSite::SaveObject\n"));
	}

	STDMETHOD(GetMoniker)(DWORD /*dwAssign*/, DWORD dwWhichMoniker, IMoniker** ppmk)
	{
		return E_NOTIMPL;
	}

	STDMETHOD(GetContainer)(IOleContainer** ppContainer)
	{
		HRESULT hr = E_POINTER;
		if (ppContainer != NULL) {
			hr = static_cast<T*>(this)->QueryInterface(__uuidof(IOleContainer), reinterpret_cast<void**>(ppContainer));
		}
		return hr;
	}

	STDMETHOD(ShowObject)(void)
	{
		HWND hWnd = m_pAxHostDelegate->GetAxHostWindow();
		HRESULT hr = E_FAIL;
		if (::IsWindow(hWnd) && ::IsRectEmpty(&m_rcPos) == FALSE)
		{
			m_bVisible = true;
			if (m_bInplaceActive)
			{
				m_pAxHostDelegate->OnAxInvalidate(&m_rcPos, TRUE);
				hr = S_OK;
			}
		}
		return hr;
	}

	STDMETHOD(OnShowWindow)(BOOL fShow)
	{
		m_bVisible = (fShow != FALSE);
		return S_OK;
	}

	STDMETHOD(RequestNewObjectLayout)(void)
	{
		ATLTRACENOTIMPL(_T("ActiveXSite::RequestNewObjectLayout\n"));
	}


	///////////////////////////////////////////////////////////////////////////
	// IOleControlSite
	STDMETHOD(OnControlInfoChanged)(void)
	{
		return S_OK;
	}

	STDMETHOD(LockInPlaceActive)(BOOL /*fLock*/)
	{
		ATLTRACENOTIMPL(_T("ActiveXSite::LockInPlaceActive\n"));
	}

	STDMETHOD(GetExtendedControl)(IDispatch**)
	{
		ATLTRACENOTIMPL(_T("ActiveXSite::GetExtendedControl\n"));
	}

	STDMETHOD(TransformCoords)(POINTL* /*pPtlHimetric*/, POINTF* /*pPtfContainer*/, DWORD /*dwFlags*/)
	{
		ATLTRACENOTIMPL(_T("ActiveXSite::TransformCoords\n"));
	}

	STDMETHOD(TranslateAccelerator)(MSG* /*pMsg*/, DWORD /*grfModifiers*/)
	{
		ATLTRACENOTIMPL(_T("ActiveXSite::TranslateAccelerator\n"));
	}

	STDMETHOD(OnFocus)(BOOL fGotFocus)
	{
		// ignore this, controls should use SetFocus(flag) instead
		return S_OK;
	}

	STDMETHOD(ShowPropertyFrame)(void)
	{
		ATLTRACENOTIMPL(_T("ActiveXSite::ShowPropertyFrame\n"));
	}


	///////////////////////////////////////////////////////////////////////////
	// IOleInPlaceSiteWindowless
	STDMETHOD(CanWindowlessActivate)(void)
	{
		// support Windowless activation
		return S_OK;
	}

	STDMETHOD(GetCapture)(void)
	{
		HRESULT hr = m_bCaptured ? S_OK : S_FALSE;
		return hr;
	}

	STDMETHOD(SetCapture)(BOOL fCapture)
	{
		if (fCapture != m_bCaptured)
		{
			m_bCaptured = fCapture;
			m_pAxHostDelegate->OnAxSetCapture(fCapture);
		}
		return S_OK;
	}

	STDMETHOD(GetFocus)(void)
	{
		return m_bFocused ? S_OK : S_FALSE;
	}

	STDMETHOD(SetFocus)(BOOL fFocus)
	{
		if (m_bFocused != fFocus)
		{
			m_bFocused = fFocus;
			InvalidateRect(NULL, true);
		}

		return S_OK;
	}

	STDMETHOD(GetDC)(LPCRECT pRect, DWORD grfFlags, HDC* phDC)
	{
		return m_pAxHostDelegate->OnAxGetDC(pRect, grfFlags, phDC);
	}

	STDMETHOD(ReleaseDC)(HDC hDC)
	{
		return m_pAxHostDelegate->OnAxReleaseDC(hDC);
	}

	STDMETHOD(InvalidateRect)(LPCRECT pRect, BOOL fErase)
	{
		// Blindly honor the specified region
		// well behaving control should pass a valid intersect rect or NULL
		if (pRect == NULL) pRect = &m_rcPos;
		m_pAxHostDelegate->OnAxInvalidate(pRect, fErase);
		return S_OK;
	}

	STDMETHOD(InvalidateRgn)(HRGN /*hRGN*/, BOOL fErase)
	{
		return InvalidateRect(NULL, fErase);
	}

	STDMETHOD(ScrollRect)(INT /*dx*/, INT /*dy*/, LPCRECT /*pRectScroll*/, LPCRECT /*pRectClip*/)
	{
		ATLTRACENOTIMPL(_T("ActiveXSite::ScrollRect\n"));
	}

	STDMETHOD(AdjustRect)(LPRECT /*prc*/)
	{
		ATLTRACENOTIMPL(_T("ActiveXSite::AdjustRect\n"));
	}

	STDMETHOD(OnDefWindowMessage)(UINT msg, WPARAM wParam, LPARAM lParam, LRESULT* plResult)
	{
		*plResult = DefWindowProc(m_pAxHostDelegate->GetAxHostWindow(), msg, wParam, lParam);
		return S_OK;
	}

	// IOleInPlaceSiteEx
	STDMETHOD(OnInPlaceActivateEx)(BOOL* pfNoRedraw, DWORD dwFlags)
	{
		HRESULT hr = S_FALSE;
		OleLockRunning(m_spOleObject, TRUE, FALSE);
		if (pfNoRedraw) {
			*pfNoRedraw = FALSE;
		}

		if ((dwFlags & ACTIVATE_WINDOWLESS) != 0) {
			m_spOleObjectWindowless = m_spControl;

			if (m_spOleObjectWindowless != NULL) {
				m_bInplaceActive = true;
				hr = S_OK;
			}
		}
		else {
			// object is not windowless, ensure we won't dispatch message to this control
			m_bInplaceActive = false;
		}
		return hr;
	}

	STDMETHOD(OnInPlaceDeactivateEx)(BOOL /*fNoRedraw*/)
	{
		m_bInplaceActive = false;
		return S_OK;
	}

	STDMETHOD(RequestUIActivate)(void)
	{
		return S_OK;
	}

	// IOleInPlaceSite
	STDMETHOD(CanInPlaceActivate)(void)
	{
		return S_OK;
	}

	STDMETHOD(OnInPlaceActivate)(void)
	{
		return S_OK;
	}

	STDMETHOD(OnUIActivate)(void)
	{
		return S_OK;
	}

	STDMETHOD(GetWindowContext)(IOleInPlaceFrame** ppFrame,
		IOleInPlaceUIWindow** ppDoc,
		LPRECT lprcPosRect,
		LPRECT lprcClipRect,
		LPOLEINPLACEFRAMEINFO lpFrameInfo)
	{
		if (ppFrame) {
			*ppFrame = NULL;
		}
		if (ppDoc) {
			*ppDoc = NULL;
		}
		if (lprcPosRect) {
			memcpy(lprcPosRect, &m_rcPos, sizeof(m_rcPos));
		}
		if (lprcClipRect) {
			memcpy(lprcClipRect, &m_rcPos, sizeof(m_rcPos));
		}
		if (lpFrameInfo) {
			lpFrameInfo->fMDIApp = FALSE;
			lpFrameInfo->hwndFrame = m_pAxHostDelegate->GetAxHostWindow();
			lpFrameInfo->haccel = NULL;
			lpFrameInfo->cAccelEntries = 0;
		}
		return S_OK;
	}

	STDMETHOD(Scroll)(SIZE /*scrollExtant*/)
	{
		ATLTRACENOTIMPL(_T("ActiveXSite::Scroll\n"));
	}

	STDMETHOD(OnUIDeactivate)(BOOL /*fUndoable*/)
	{
		return S_OK;
	}

	STDMETHOD(OnInPlaceDeactivate)(void)
	{
		return OnInPlaceDeactivateEx(TRUE);
	}

	STDMETHOD(DiscardUndoState)(void)
	{
		ATLTRACENOTIMPL(_T("ActiveXSite::DiscardUndoState"));
	}

	STDMETHOD(DeactivateAndUndo)(void)
	{
		ATLTRACENOTIMPL(_T("ActiveXSite::DeactivateAndUndo"));
	}

	STDMETHOD(OnPosRectChange)(LPCRECT lprcPosRect)
	{
		HRESULT hr = E_UNEXPECTED;
		hr = S_OK;
		if (lprcPosRect && memcmp(&m_rcPos, lprcPosRect, sizeof(m_rcPos)) != 0)
		{
			if (m_spInPlaceObject)
			{
				memcpy(&m_rcPos, lprcPosRect, sizeof(m_rcPos));
				hr = m_spInPlaceObject->SetObjectRects(&m_rcPos, &m_rcPos);
			}
			if (SUCCEEDED(hr))
			{
				hr = SetExtent(m_rcPos.right - m_rcPos.left, m_rcPos.bottom - m_rcPos.top);
			}
		}
		return hr;
	}

	// IOleWindow
	STDMETHOD(GetWindow)(HWND* phwnd)
	{
		//
		*phwnd = m_pAxHostDelegate->GetAxHostWindow();
		return S_OK;
	}

	STDMETHOD(ContextSensitiveHelp)(BOOL /*fEnterMode*/)
	{
		ATLTRACENOTIMPL(_T("ActiveXSite::ContextSensitiveHelp\n"));
	}


	// IAdviseSink
	STDMETHOD_(void, OnDataChange)(FORMATETC* /*pFormatetc*/, STGMEDIUM* /*pStgmed*/)
	{
	}

	STDMETHOD_(void, OnViewChange)(DWORD /*dwAspect*/, LONG /*lindex*/)
	{
	}

	STDMETHOD_(void, OnRename)(IMoniker* /*pmk*/)
	{
	}

	STDMETHOD_(void, OnSave)(void)
	{
	}

	STDMETHOD_(void, OnClose)(void)
	{
	}

protected:
	HRESULT SetExtent(int width, int height)
	{
		HRESULT hr = E_UNEXPECTED;
		if (m_spOleObject != NULL)
		{
			SIZEL pxsize = { width, height };
			SIZEL hmsize = { 0 };
			SPixelToHiMetric(&pxsize, &hmsize);
			hr = m_spOleObject->SetExtent(DVASPECT_CONTENT, &hmsize);
			hr = m_spOleObject->GetExtent(DVASPECT_CONTENT, &hmsize);
			SHiMetricToPixel(&hmsize, &pxsize);
			m_rcPos.right = m_rcPos.left + pxsize.cx;
			m_rcPos.bottom = m_rcPos.top + pxsize.cy;
		}
		return hr;
	}

	HRESULT DoInplaceActivate()
	{
		HRESULT hr;
		m_pAxHostDelegate->OnAxActivate(m_spControl);
		m_dwViewObjectType = 0;
		hr = m_spOleObject->QueryInterface(__uuidof(IViewObjectEx), reinterpret_cast<void**>(&m_spViewObject));
		if (FAILED(hr))
		{
			hr = m_spOleObject->QueryInterface(__uuidof(IViewObject2), reinterpret_cast<void**>(&m_spViewObject));
			if (SUCCEEDED(hr))
				m_dwViewObjectType = 3;
		}
		else {
			m_dwViewObjectType = 7;
		}

		if (FAILED(hr))
		{
			hr = m_spOleObject->QueryInterface(__uuidof(IViewObject), reinterpret_cast<void**>(&m_spViewObject));
			if (SUCCEEDED(hr))
				m_dwViewObjectType = 1;
		}
		SComQIPtr<IAdviseSink> advise_sink(m_spControl);
		m_spOleObject->Advise(advise_sink, &m_dwOleObjSink);
		if (m_spViewObject)
			hr = m_spViewObject->SetAdvise(DVASPECT_CONTENT, 0, advise_sink);
		m_spOleObject->SetHostNames(OLESTR("SOUIAXWIN"), NULL);

		// Do Inplace activate if possible
		hr = DoVerb(OLEIVERB_INPLACEACTIVATE);
		return hr;
	}


private:
	bool                            m_bVisible;
	bool                            m_bInplaceActive;
	BOOL                            m_bFocused;
	BOOL                            m_bCaptured;
	DWORD                           m_dwMiscStatus;
	DWORD                           m_dwViewObjectType;
	DWORD                            m_dwOleObjSink;
	DWORD                            m_grfFlags;

	RECT                            m_rcPos;
	SComPtr<IUnknown>               m_spControl;
	SComPtr<IViewObjectEx>          m_spViewObject;
	SComQIPtr<IOleObject>           m_spOleObject;
	SComQIPtr<IOleInPlaceObject>    m_spInPlaceObject;
	SComQIPtr<IOleInPlaceObjectWindowless> m_spOleObjectWindowless;
	SComPtr<IDocHostUIHandler>        m_spDocHostUIHandler;
	IAxHostDelegate* m_pAxHostDelegate;
};





/**
 * ActiveXContainerImpl
 */
class  SAxContainer : public IOleContainer,
	public IBindHost,
	public IServiceProvider,
	public MinimumIDispatchImpl,
	public ActiveXSite<SAxContainer>
{
public:

	SAxContainer();
	virtual ~SAxContainer();

	BOOL CreateControl(REFGUID guid, DWORD dwClsCtx = CLSCTX_INPROC_SERVER);

	///////////////////////////////////////////////////////////////////////////
	// IOleContainer
	STDMETHOD(EnumObjects)(DWORD /*grfFlags*/, IEnumUnknown** /*ppenum*/)
	{
		ATLTRACENOTIMPL(_T("ActiveXContainerImpl::EnumObjects\n"));
	}

	STDMETHOD(LockContainer)(BOOL /*fLock*/)
	{
		ATLTRACENOTIMPL(_T("ActiveXContainerImpl::LockContainer\n"));
	}

	// IOleContainer::IParseDisplayName
	STDMETHOD(ParseDisplayName)(IBindCtx* /*pbc*/, LPOLESTR /*pszDisplayName*/,
		ULONG* /*pchEaten*/, IMoniker** /*ppmkOut*/)
	{
		ATLTRACENOTIMPL(_T("ActiveXContainerImpl::ParseDisplayName\n"));
	}


	// IBindHost
	STDMETHOD(CreateMoniker)(LPOLESTR szName, IBindCtx* pBC,
		IMoniker** ppmk, DWORD dwReserved);
	STDMETHOD(MonikerBindToStorage)(IMoniker* pMk, IBindCtx* pBC,
		IBindStatusCallback* pBSC, REFIID riid, void** ppvObj);
	STDMETHOD(MonikerBindToObject)(IMoniker* pMk, IBindCtx* pBC,
		IBindStatusCallback* pBSC, REFIID riid, void** ppvObj);

	// IServiceProvider
	STDMETHOD(QueryService)(REFGUID guidService, REFIID riid, void** ppvObject);

	STDMETHOD(QueryInterface)(REFIID iid, void** object);
	virtual ULONG STDMETHODCALLTYPE AddRef();
	virtual ULONG STDMETHODCALLTYPE Release();

private:
	LONG            m_lRefCnt;
};


SNSEND

#endif //ATLACTIVEXCONTAINER_HPP